YOLOv5

您所在的位置:网站首页 yolov5 608 YOLOv5

YOLOv5

2022-05-08 14:09| 来源: 网络整理| 查看: 265

集智书童推荐搜索关键词列表:cuda项目实践论文解读TransformerImage

YOLOv5 Lite在YOLOv5的基础上进行一系列消融实验,使其更轻(Flops更小,内存占用更低,参数更少),更快(加入shuffle channel,yolov5 head进行通道裁剪,在320的input_size至少能在树莓派4B上的推理速度可以达到10+FPS),更易部署(摘除Focus层和4次slice操作,让模型量化精度下降在可接受范围内)。

Image1输入端方法1、Mosaic数据增强

YOLOv5 Lite的输入端采用了和YOLOv5、YOLOv4一样的Mosaic数据增强的方式。其实Mosaic数据增强的作者也是来自YOLOv5团队的成员,不过,随机缩放、随机裁剪、随机排布的方式进行拼接,对于小目标的检测效果还是很不错的。

Image

为什么要进行Mosaic数据增强呢?

在平时训练模型时,一般来说小目标的AP比中目标和大目标低很多。而Coco数据集中也包含大量的小目标,但比较麻烦的是小目标的分布并不均匀。

首先看下小、中、大目标的定义:

Image

可以看到小目标的定义是目标框的长宽0×0~32×32之间的物体。

Image

但在整体的数据集中,小、中、大目标的占比并不均衡。

如上表所示,Coco数据集中小目标占比达到41.4%,数量比中目标和大目标都要多。但在所有的训练集图片中,只有52.3%的图片有小目标,而中目标和大目标的分布相对来说更加均匀一些。

针对上述状况采用了Mosaic数据增强的方式,主要有几个优点:

丰富数据集

随机使用4张图片,随机缩放,再随机分布进行拼接,大大丰富了检测数据集,特别是随机缩放增加了很多小目标,让网络的鲁棒性更好。

减少GPU使用

可能会有人说,随机缩放和普通的数据增强也可以做到类似的效果,在同等size的输入下,普通的数据增强只能看到一张图像,而Mosaic增强训练时可以直接计算4张图片的数据,这样即使一个GPU也可以达到比较好的效果。

2、自适应Anchor计算

YOLOv5 Lite依旧沿用YOLOv5的Anchor计算方式,我们知道,在YOLO算法之中,针对不同的数据集,都会设置固定的Anchor。

在网络训练中,网络在初始锚框的基础上输出预测框,进而和Ground Truth进行比对,计算两者差距,再反向更新,迭代网络参数。

可以看出Anchor也是比较重要的一部分,比如Yolov5在Coco数据集上初始设定的锚框:

Image

第1行是在最大的特征图上的锚框;

第2行是在中间的特征图上的锚框;

第3行是在最小的特征图上的锚框;

自适应计算Anchor的流程如下:载入数据集,得到数据集中所有数据的wh;将每张图片中wh的最大值等比例缩放到指定大小img_size,较小边也相应缩放;将bboxes从相对坐标改成绝对坐标(乘以缩放后的wh);筛选bboxes,保留wh都大于等于两个像素的bboxes;使用k-means聚类得到n个anchors(掉k-means包 涉及一个白化操作);使用遗传算法随机对anchors的wh进行变异,如果变异后效果变得更好(使用anchor_fitness方法计算得到的fitness(适应度)进行评估)就将变异后的结果赋值给anchors,如果变异后效果变差就跳过,默认变异1000次;1.3 自适应缩放图片

在常用的目标检测算法中,不同的图片长宽都不相同,因此常用的方式是将原始图片统一缩放到一个标准尺寸,再送入检测网络中。比如Yolo算法中常用416×416,608×608等尺寸。

但Yolov5代码中对此进行了改进,也是Yolov5推理速度能够很快的一个不错的trick。作者认为,在项目实际使用时,很多图片的长宽比不同。因此缩放填充后,两端的黑边大小都不同,而如果填充的比较多,则存在信息冗余,影响推理速度。

图像高度上两端的黑边变少了,在推理时,计算量也会减少,即目标检测速度会得到提升。

Image

下面根据上图进行计算一下,主要是展示推理时的计算:

计算缩放比例

原始图像的尺寸为640×427,与640的输入尺寸计算得到2个缩放系数分别为1.0和1.499,这里选择较小的1.0参与缩放计算;

计算缩放后的尺寸

这里将原始尺寸乘以缩放系数1.0,可以分别得到长宽为640,427

计算灰边填充数值

640-427=213,得到原本需要填充的高度。再采用numpy中np.mod取余数的方式,得到21个像素,再除以2,即得到图片高度两端需要填充的数值(为10【向上取整】和11【向下取整】),于是得到推理结果的尺寸为640×448。

注意

只是在测试,使用模型推理时,才采用缩减灰边的方式,提高目标检测,推理的速度。为什么np.mod函数的后面用32?因为Yolov5的网络经过5次下采样,而2的5次方,等于32。所以至少要去掉32的倍数,以免产生尺度太小走不完stride的问题,再进行取余。

2模型架构Image目标检测全流程ImageYOLOv5 Lite-E模型架构图3.1 去除Focus层

为了充分理解,先来回顾一下Focus这个OP吧:

class Focus(nn.Module):    # Focus wh information into c-space    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups        super().__init__()        self.conv = Conv(c1 * 4, c2, k, s, p, g, act)           def forward(self, x):  # x(b,c,w,h) -> y(b,4c,w/2,h/2)        return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))

从直观理解,其实就是将图像进行切片,类似于下采样取值,这样得到4个图像:

Image

相当于是将空间信息绕到了通道信息中,cat后输入通道变成4倍,再通过conv得到新的featuremap,这样做的好处就是保持了下采样的信息没有丢失,图像的信息都保留了下来,但是在浅层中的应用,作者也表示了单纯是从计算量和参数量的角度上去设计,因为如上的所述的信息保存在浅层的意义并不大。所以后来查了下,作者的设计原因就是:为了减少浮点数和提高速度,而不是增加featuremap。

故,1个Focus可以替代更多的卷积层,而将空间信息聚焦到通道空间,这也会减少1像素的回归精度,而大部分的检测回归精度都不会接近1,这也是为什么Focus要放在输入的第1层上的原因。

一句话解释:Focus为了压缩网络层去提速。但是在YOLOv5 V6.0中,作者经过试验后,结论就是使用卷积替代Focus获得了更好的性能,且没有之前的一些局限性和副作用,因此此后的迭代中YOLO v5均去除了Focus操作。

YOLOv5 Lite也在此之前也不约而同的选择了摘除Focus层,避免多次采用slice操作,对于的芯片,特别是不含GPU、NPU加速的芯片,频繁的slice操作只会让缓存占用严重,加重计算处理的负担。同时,在芯片部署的时候,Focus层的转化对新手极度不友好。

3.2 ShuffleNet Backbone

YOLOv5 Lite的Backbone选择的是ShuffleNet,为什么是ShuffleNet呢?

这里给出轻量化设计的4个准则:

同等通道大小可以最小化内存访问量;过量使用组卷积会增加MAC;网络过于碎片化(特别是多路)会降低并行度;不能忽略元素级操作(比如shortcut和Add)。

同时,YOLOv5 Lite避免多次使用C3 Leyer以及高通道的C3 Layer

C3 Leyer是YOLOv5作者提出的CSPBottleneck改进版本,它更简单、更快、更轻,在近乎相似的损耗上能取得更好的结果。但C3 Layer采用多路分离卷积,测试证明,频繁使用C3 Layer以及通道数较高的C3 Layer,占用较多的缓存空间,减低运行速度。

为什么通道数越高的C3 Layer会对cpu不太友好?,主要还是因为Shufflenetv2的G1准则,通道数越高,hidden channels与c1、c2的阶跃差距更大,来个不是很恰当的比喻,想象下跳一个台阶和十个台阶,虽然跳十个台阶可以一次到达,但是你需要助跑,调整,蓄力才能跳上,可能花费的时间更久

ImageBackbone

针对ShuffleNet v2,作者首先复盘了ShuffleNetV1的问题,认为目前比较关键的问题是如何在全卷积或者分组卷积中维护大多数的卷积是平衡的。针对这个目标,作者提出了Channel Split的操作,同时构建了ShuffleNetV2。

Image

上图中(a)(b)是ShuffleNetV1的结构,而后面的(c)(d)是ShuffleNetV2的层结构,也是YOLOv5 Lite中的主要结构,分别对应的是结构图中的SFB1_X和SFB2_X

ImageSFB1_X结构对应图(d)结构ImageSFB2_X结构对应图(c)结构

下面稍微讲一下笔者结合论文的理解:

Channel Split操作将整个特征图分为c’组(假设为A组)和c-c’(假设为B组)两个部分,主要有3个好处:

整个特征图分为2个组了,但是这样的分组又不像分组卷积一样,增加了卷积时的组数,符合准则2;这样分开之后,将A组认为是通过short-cut通道的,而B组经过的bottleneck层的输入输出的通道数就可以保持一致,符合准则1;

同时由于最后使用的concat操作,没有用TensorAdd操作,符合准则4;

可以看到,这样一个简单的通道分离的操作带来了诸多好处;但是从理论上来说,这样的结构是否还符合short-cut的初衷(即bottleneck学到的是残差Residual部分)?这里笔者也不好妄加揣测,但是可以想到的是经过后面的Channel Shuffle的乱序之后,每个通道应该都会经过一次bottleneck结构。

上述的结构是不改变输入输出通道数和特征图大小的情况,而池化操作使用图(d)代替了,跟ShuffleNetV1类似,经过这样的结构之后,图像通道数扩张为原先的2倍。

YOLO v5 Lite在Backbone中还摘除shufflenetv2 backbone的1024 conv 和 5×5 pooling。

3.3 Neck

在目标检测领域,为了更好的提取融合特征,通常在Backbone和输出层,会插入一些层,这个部分称为Neck。相当于目标检测网络的颈部,也是非常关键的。

而YOLO v5 Lite也不例外的使用了FPN+PAN的结构,但是Lite对yolov5 head进行通道剪枝,剪枝细则参考了ShuffleNet v2的设计准则,同时改进了YOLOv4中的FPN+PAN的结构,具体就是:

为了最优化内存的访问和使用,选择了使用相同的通道数量(e模型Neck通道为96);为了进一步优化内存的使用,选择了使用原始的PANet结构,还原YOLOv4的cat操作为add操作;ImageFPN+PAN的结构ImageYOLOv5 Lite的Neck层ImageYOLOv5 Lite的FPN+PAN整体结构图

这样结合操作,FPN层自顶向下传达强语义特征(High-Level特征),而特征金字塔则自底向上传达强定位特征(Low-Level特征),两两联手,从不同的主干层对不同的检测层进行特征聚合。

FPN+PAN借鉴的是18年CVPR的PANet,当时主要应用于图像分割领域,但Alexey将其拆分应用到Yolov4中,进一步提高特征提取的能力。

Image3.4 Head

对于Head部分,YOLO V5 Lite并没有对YOLOv5进行改进,所以可以看到三个紫色箭头处的特征图是40×40、20×20、10×10。以及最后Prediction中用于预测的3个特征图:

①==>40×40×255

②==>20×20×255

③==>10×10×255

Image3.5 Anchor机制及坐标变换1、Anchor机制

对于YOLOv5,Anchor对应与Yolov3则恰恰相反,对于所设置的Anchor:

第一个Yolo层是最大的特征图40×40,对应最小的anchor box。第二个Yolo层是中等的特征图20×20,对应中等的anchor box。第三个Yolo层是最小的特征图10×10,对应最大的anchor box。# anchors:#   - [10,13, 16,30, 33,23]  # P3/8#   - [30,61, 62,45, 59,119]  # P4/16#   - [116,90, 156,198, 373,326]  # P5/322、样本匹配策略

在yolo v3&v4中,Anchor匹配策略和SSD、Faster RCNN类似:保证每个gt bbox有一个唯一的Anchor进行对应,匹配规则就是IOU最大,并且某个gt不能在三个预测层的某几层上同时进行匹配。不考虑一个gt bbox对应多个Anchor的场合,也不考虑Anchor是否设置合理。

这里先说一下YOLOv3的匹配策略:

假设一个图中有一个目标,这个被分割成三种格子的形式,分割成13×13 、26 × 26、52 × 52 。

这个目标中心坐标下采样8倍,(416/8=52),会落在 52 × 52 这个分支的所有格子中的某一个格子,落在的格子会产生3个anchor,3个anchor和目标(已经下采样8倍的目标框)分别计算iou,得到3个iou,凡是iou大于阈值0.3的,就记为正样本,就会将label[0]中这个iou大于0.3的anchor的相应位置 赋上真实框的值。

这个目标中心坐标下采样16倍,(416/16=26),会落在 26 × 26 这个分支的所有格子中的某一个格子,落在的格子会产生3个anchor,3个anchor和目标(已经下采样16倍的目标框)分别计算iou,得到三个iou,凡是iou大于阈值0.3的,就记为正样本,就会将label[1]中这个iou大于0.3的anchor的相应位置 赋上真实框的值。

这个目标中心坐标下采样32倍,(416/32=13),会落在 13 × 13 这个分支的所有格子中的某一个格子,落在的格子会产生3个anchor,3个anchor和目标(已经下采样32倍的目标框)分别计算iou,得到三个iou,凡是iou大于阈值0.3的,就记为正样本,就会将label[2]中这个iou大于0.3的anchor的相应位置 赋上真实框的值。

如果目标所有的anchor,9个anchor,iou全部小于阈值0.3,那么选择9个anchor中和下采样后的目标框iou最大的,作为正样本,将目标真实值赋值给相应的anchor的位置。

总的来说,就是将目标先进行3种下采样,分别和目标落在的网格产生的 9个anchor分别计算iou,大于阈值0.3的记为正样本。如果9个iou全部小于0.3,那么和目标iou最大的记为正样本。对于正样本,我们在label上 相对应的anchor位置上,赋上真实目标的值。

而yolov5采用了跨网格匹配规则,增加正样本Anchor数目的做法:

对于任何一个输出层,yolov5抛弃了Max-IOU匹配规则而采用shape匹配规则,计算标签box和当前层的anchors的宽高比,即:wb/wa,hb/ha。如果宽高比大于设定的阈值说明该box没有合适的anchor,在该预测层之间将这些box当背景过滤掉。

# r为目标wh和锚框wh的比值,比值在0.25到4之间的则采用该种锚框预测目标r = t[:, :, 4:6] / anchors[:, None]  # wh ratio:计算标签box和当前层的anchors的宽高比,即:wb/wa,hb/ha# 将比值和预先设置的比例anchor_t对比,符合条件为True,反之Falsej = torch.max(r, 1 / r).max(2)[0]  0:        BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g)   。。。。。。

4.3、后处理之DIoU NMSImageImageImage

在上图重叠的摩托车检测中,中间的摩托车因为考虑边界框中心点的位置信息,也可以回归出来。因此在重叠目标的检测中,DIOU_nms的效果优于传统的nms。

为什么不用CIoU NMS呢?

因为前面讲到的CIOU loss,是在DIOU loss的基础上,添加的影响因子,包含ground truth标注框的信息,在训练时用于回归。但在测试过程中,并没有ground truth的信息,不用考虑影响因子,因此直接用DIOU NMS即可。

4YOLOv5 Lite训练自己的数据集5.1 git clone仓库代码

clone YOLOv5 Lite代码并下载coco的预训练权重。

$ git clone https://github.com/ppogg/YOLOv5-Lite$ cd YOLOv5-Lite$ pip install -r requirements.txt5.2 处理数据集格式

这里可以直接参考coco128的数据集形式进行整理:

文件夹目录如下图所示:

ImageImage5.3 配置超参数

主要是配置data文件夹下的coco128.yaml中的数据集位置和种类:

ImageImage5.4 配置模型

这里主要是配置models目录下的模型yaml文件,主要是进去后修改nc这个参数来进行类别的修改。

Image修改nc参数

目前支持的模型种类如下所示:

Image5.3 训练$ python train.py --data coco.yaml --cfg v5lite-e.yaml --weights v5lite-e.pt --batch-size 128                                         v5lite-s.yaml --weights v5lite-s.pt --batch-size 128                                         v5lite-c.yaml           v5lite-c.pt               96                                         v5lite-g.yaml           v5lite-g.pt               64

如果您是多卡进行训练,则:

$ python -m torch.distributed.launch --nproc_per_node 2 train.py5.4 检测结果$ python path/to/detect.py --weights v5lite-e.pt --source 0 img.jpg        # imageImage检测结果5TensorRT部署5.1 目标检测常见的落地形式Image1、TensorRT是什么

TensorRT是推理优化器,能对训练好的模型进行优化。可以理解为只有前向传播的深度学习框架,这个框架可以将Caffe,TensorFlow的网络模型解析,然后与TensorRT中对应的层进行一一映射,把其他框架的模型统一全部转换到TensorRT中,然后在TensorRT中可以针对NVIDIA自家GPU实施优化策略,并进行部署加速。当你的网络训练完之后,可以将训练模型文件直接丢进TensorRT中,而不再需要依赖深度学习框架(Caffe,TensorFlow等)。

2、本文AI部署流程

先把onnx转化为TensorRT的Engine文件,然后让c++环境下的TensorRT直接加载Engine文件,从而构建engine,本文主要讲解onnx转换至Engine,然后进行基于TensorRT的C++推理检测。

Image

转换和部署模型5个基本步骤:

step1:获取模型step2:选择batchsizestep3:选择精度step4:模型转换step5:模型部署5.2 ONNX-TensorRT的部署流程1、ONNX转化为TRT Engine# 导出onnx文件python export.py ---weights weights/v5lite-g.pt --batch-size 1 --imgsz 640 --include onnx --simplify

# 使用TensorRT官方的trtexec工具将onnx文件转换为enginetrtexec --explicitBatch --onnx=./v5lite-g.onnx --saveEngine=v5lite-g.trt --fp16

闲话不多说,这里已经拿到了trt的engine,那么如何进行推理呢?总的来说,分为3步:

首先load你的engine,拿到一个ICudaEngine, 这个是TensorRT推理的核心;定位模型的输入和输出,有几个输入有几个输出;forward模型,然后拿到输出,对输出进行后处理。

当然这里最核心的东西其实就两个,一个是如何导入拿到CudaEngine,第二个是比较麻烦的后处理。

2、加载TRT Enginebool Model::readTrtFile() {    std::string cached_engine;    std::fstream file;    std::cout 


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3